package com.specter.serv.service.impl;

import java.security.Security;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.engines.SM4Engine;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.util.CollectionUtils;

import com.specter.serv.service.SureService;

import lombok.extern.slf4j.Slf4j;

/**
 * 数据机密性及完整性服务（SM4/SM3实现）
 * 
 * @author Liang.Wang
 * @since 2025-02-28
 * @version 1.0
 */
@Slf4j
@ConditionalOnProperty(prefix = "thisapp.xdata.sure", name = "type", havingValue = "sms")
public class SureServiceImpl implements SureService {
    private String iv = "4233660099121314"; // 向量16长度
    private String ky = "liangthinkdesign"; // 标识
    private boolean inited = false;

    public SureServiceImpl() {
        if (!inited) {
            Security.addProvider(new BouncyCastleProvider());
            inited = true;
        }
        log.info("SureService init with SMS !");
    }

    @Override
    public String encrypt(String plains) {
        if (StringUtils.isBlank(plains)) {
            return plains;
        }
        PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new SM4Engine()));
        cipher.init(true, new ParametersWithIV(new KeyParameter(ky.getBytes()), iv.getBytes()));
        byte[] input = plains.getBytes();
        byte[] output = new byte[cipher.getOutputSize(input.length)];
        int length = cipher.processBytes(input, 0, input.length, output, 0);
        try {
            cipher.doFinal(output, length);
        } catch (DataLengthException | IllegalStateException | InvalidCipherTextException e) {
            e.printStackTrace();
        }
        return Hex.toHexString(output);
    }

    @Override
    public String decrypt(String secret) {
        if (StringUtils.isBlank(secret)) {
            return secret;
        }
        PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new SM4Engine()));
        cipher.init(false, new ParametersWithIV(new KeyParameter(ky.getBytes()), iv.getBytes()));
        byte[] input = Hex.decode(secret);
        byte[] output = new byte[cipher.getOutputSize(input.length)];
        int length = cipher.processBytes(input, 0, input.length, output, 0);
        try {
            cipher.doFinal(output, length);
        } catch (DataLengthException | IllegalStateException | InvalidCipherTextException e) {
            e.printStackTrace();
        }
        return new String(Arrays.copyOfRange(output, 0, length));
    }

    @Override
    public List<String> encrypt(List<String> list) {
        if (CollectionUtils.isEmpty(list)) {
            return list;
        }
        return list.stream().map(this::encrypt).toList();
    }

    @Override
    public List<String> decrypt(List<String> list) {
        if (CollectionUtils.isEmpty(list)) {
            return list;
        }
        return list.stream().map(this::decrypt).toList();
    }

    @Override
    public String hmac(String text) {
        if (StringUtils.isBlank(text)) {
            return text;
        }
        HMac hmac = new HMac(new SM3Digest());
        hmac.init(new KeyParameter(ky.getBytes()));
        byte[] inputBytes = text.getBytes();
        hmac.update(inputBytes, 0, inputBytes.length);
        byte[] hmacResult = new byte[hmac.getMacSize()];
        hmac.doFinal(hmacResult, 0);
        return Hex.toHexString(hmacResult);
    }

    @Override
    public boolean vmac(String text, String hmac) {
        if (StringUtils.isAnyBlank(text, hmac)) {
            return false;
        }
        return hmac.equals(hmac(text));
    }

}
